package com.aim.analysis.excel;

import com.aim.analysis.artifact.AbstractArtifact;
import com.aim.analysis.artifact.ArtifactSubFolder;
import com.aim.analysis.questionnaire.QValueExtractor;
import com.aim.analysis.records.Game;
import com.aim.analysis.records.GameValueExtractor;
import com.aim.analysis.records.Hit;
import com.aim.analysis.records.HitValueExtractor;
import com.aim.util.extractors.ValueExtractor;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

/**
 * User: Avraham Shvartzon
 * Date: 12/15/14
 */
public abstract class HitReport extends AbstractArtifact {

    private String fileSuffix;

    public HitReport(String fileSuffix) {
        this.fileSuffix = fileSuffix;
    }

    @Override
    protected String getFileNameSuffix() {
        return fileSuffix;
    }

    @Override
    public ArtifactSubFolder getArtifactSubFolder() {
        return ArtifactSubFolder.RECORD_PER_HIT;
    }

    protected static List<HitValueExtractor<Double>> getHitExtractor() {
        List<HitValueExtractor<Double>> extractors = new LinkedList<HitValueExtractor<Double>>();
        extractors.add(HitValueExtractor.netScoreIncrement);
        return extractors;
    }

    protected static List<QValueExtractor> getQuestionnaireExtractors() {
        List<QValueExtractor> extractors = new LinkedList<QValueExtractor>();

        extractors.add(QValueExtractor.partitionKey);
        extractors.add(QValueExtractor.gender);
        extractors.add(QValueExtractor.age);
        extractors.add(QValueExtractor.enjoyGame);
        extractors.add(QValueExtractor.performanceSelfAssessment);
        extractors.add(QValueExtractor.alertFrequency);
        extractors.add(QValueExtractor.setFrequency);
        extractors.add(QValueExtractor.secondsInAdvanceForAlert);
        extractors.add(QValueExtractor.maintenanceAlertsHelpful);
        extractors.add(QValueExtractor.gameStrategyDesc);
        extractors.add(QValueExtractor.takeRisks);
        extractors.add(QValueExtractor.bugsOccured);
        extractors.add(QValueExtractor.bugDescriptionIfExist);
        extractors.add(QValueExtractor.freeText);

        return extractors;
    }

    protected List<GameValueExtractor> getGameExtractors() {
        List<GameValueExtractor> valueExtractors = new ArrayList<GameValueExtractor>();

        // header
        //valueExtractors.add(GameValueExtractor.partitionKey);
        valueExtractors.add(GameValueExtractor.gameDescription);
        valueExtractors.add(GameValueExtractor.gameNumber);

        //mechanical - actions & costs
        valueExtractors.add(GameValueExtractor.maintenance);
        valueExtractors.add(GameValueExtractor.minor);
        valueExtractors.add(GameValueExtractor.moderate);
        valueExtractors.add(GameValueExtractor.severe);
//        valueExtractors.add(GameValueExtractor.maintenanceCost);
//        valueExtractors.add(GameValueExtractor.repairCost);
        valueExtractors.add(GameValueExtractor.mechanicalCost);

        // Alerts
        valueExtractors.add(GameValueExtractor.numOfAlerts);
        valueExtractors.add(GameValueExtractor.minResponseDelay);
        valueExtractors.add(GameValueExtractor.maxResponseDelay);
        valueExtractors.add(GameValueExtractor.meanResponseDelay);
        valueExtractors.add(GameValueExtractor.meanResponseDelay2);
        valueExtractors.add(GameValueExtractor.minAlertRecommendedTime);
        valueExtractors.add(GameValueExtractor.maxAlertRecommendedTime);
        valueExtractors.add(GameValueExtractor.meanAlertRecommendedTime);

        valueExtractors.add(GameValueExtractor.numOfMaintenanceAfterAlert);
        valueExtractors.add(GameValueExtractor.numOfMalfunctionAfterAlert);
        valueExtractors.add(GameValueExtractor.firstEventAfterAlertDiff);

        //refuel - actions & costs
        valueExtractors.add(GameValueExtractor.refuelByStations);
        valueExtractors.add(GameValueExtractor.refuelByStationsCost);
        valueExtractors.add(GameValueExtractor.refuelBySpaceship);
        valueExtractors.add(GameValueExtractor.refuelBySpaceshipCost);
        valueExtractors.add(GameValueExtractor.totalRefuelCost);

        //time
        valueExtractors.add(GameValueExtractor.mechanicalTime);
        valueExtractors.add(GameValueExtractor.refuelBySpaceshipTime);
        valueExtractors.add(GameValueExtractor.freezeTime);


        //performance
        valueExtractors.add(GameValueExtractor.totalShutdown);
        valueExtractors.add(GameValueExtractor.totalCollision);
        valueExtractors.add(GameValueExtractor.totalFire);

        //valueExtractors.add(GameValueExtractor.avgMSE);
        //valueExtractors.add(GameValueExtractor.segmentMSE);
        valueExtractors.add(GameValueExtractor.netScore);
        valueExtractors.add(GameValueExtractor.calculatedMechanicalCost);

        // score
        // valueExtractors.add(GameValueExtractor.score);
        valueExtractors.add(GameValueExtractor.calculatedScore);

        return valueExtractors;
    }

    @Override
    public boolean execute(Collection<Hit> hitCollection) throws Exception {
        XSSFWorkbook workbook = new XSSFWorkbook();
        writeGameDetails(workbook, hitCollection);
        writeStatistic(workbook, hitCollection);

        return writeToFile(workbook);
    }

    protected abstract void writeStatistic(XSSFWorkbook workbook, Collection<Hit> hitCollection);


    protected void writeGameDetails(XSSFWorkbook workbook, Collection<Hit> hitCollection) {
        XSSFSheet sheet = workbook.createSheet("Games details");
        List<GameValueExtractor> gameExtractors = getGameExtractors();
        List<QValueExtractor> questionnaireExtractors = getQuestionnaireExtractors();
        List<HitValueExtractor<Double>> hitExtractors = getHitExtractor();

        //1. write header
        XSSFRow header = sheet.createRow(0);

        int cellIndex = 0;
        header.createCell(cellIndex++).setCellValue("date");
        header.createCell(cellIndex++).setCellValue("workerId");
        header.createCell(cellIndex++).setCellValue("assignmentId");
        header.createCell(cellIndex++).setCellValue("score g1");

        // game columns header
        for (int i = 0; i < getNumOfGames(); i++) {
            String suffix = " " + getGameSuffix(i);
            for (GameValueExtractor gameValueExtractor : gameExtractors) {
                header.createCell(cellIndex++).setCellValue(gameValueExtractor.name + suffix);
            }
        }

        for (HitValueExtractor<Double> hitExtractor : hitExtractors) {
            header.createCell(cellIndex++).setCellValue(hitExtractor.name);
        }


        // demographic & behaviour
        for (QValueExtractor questionnaireExtractor : questionnaireExtractors) {
            header.createCell(cellIndex++).setCellValue(questionnaireExtractor.name);
        }


        //2. write content
        int rowIndex = 1;
        for (Hit hit : hitCollection) {
            XSSFRow row = sheet.createRow(rowIndex++);
            cellIndex = 0;

            // write hit data
            row.createCell(cellIndex++).setCellValue(hit.getDate().getTime());
            row.createCell(cellIndex++).setCellValue(hit.workerId);
            row.createCell(cellIndex++).setCellValue(hit.assignmentId);
            row.createCell(cellIndex++).setCellValue(hit.gameList.get(0).getCalculatedScore());

            // write games (2-3) data
//
            List<Game> orderedGames = orderingGames(hit.gameList);

            for (int i = 1; i <= getNumOfGames(); i++) {
                Game game = orderedGames.get(i);
                for (GameValueExtractor gameExtractor : gameExtractors) {
                    writeValue(cellIndex, row, game, gameExtractor, workbook);
                    cellIndex++;
                }
            }

            for (HitValueExtractor<Double> hitExtractor : hitExtractors) {
                XSSFCell cell = row.createCell(cellIndex++);
                writeValue(cell, hitExtractor, hit);
            }

            for (QValueExtractor questionnaireExtractor : questionnaireExtractors) {
                Object value = questionnaireExtractor.getValue(hit.questionnaireRecord);
                row.createCell(cellIndex++).setCellValue(value != null ? value.toString() : "N/A");
            }
        }
    }

    protected abstract int getNumOfGames();

    protected abstract List<Game> orderingGames(List<Game> gameList);

    protected abstract String getGameSuffix(int i);


    protected void writeValue(int cellIndex, XSSFRow row, Game game, GameValueExtractor gameExtractor, Workbook wb) {

        XSSFCell cell = row.createCell(cellIndex);

        if (gameExtractor == GameValueExtractor.gameDescription) {
            Font hlink_font = wb.createFont();
            hlink_font.setUnderline(Font.U_SINGLE);
            hlink_font.setColor(IndexedColors.BLUE.getIndex());
            CellStyle hlink_style = wb.createCellStyle();
            hlink_style.setFont(hlink_font);
            Hyperlink link = wb.getCreationHelper().createHyperlink(Hyperlink.LINK_FILE);
            link.setAddress(game.getImageFileName());

            cell.setHyperlink(link);
            cell.setCellStyle(hlink_style);
        }
        writeValue(cell, gameExtractor, game);
    }

    protected static <T> void writeValue(XSSFCell cell, ValueExtractor<T, ?> extractor, T obj) {
        Object value = extractor.getValue(obj);
        if (value != null && value instanceof Number) {
            cell.setCellValue(((Number) value).floatValue());
        } else {
            cell.setCellValue(value == null ? "N/A" : value.toString());
        }
    }


}
